#include "GameLogic.h"
#include <cmath>

GameLogic::GameLogic(void)
{
	//buffer = 0;
	physics = 0;
	explosion = 0;
	spring = 0;
}

GameLogic::~GameLogic(void)
{
	if(physics)
		delete physics;

	if(explosion)
		delete explosion;
}

void GameLogic::init(CL_GraphicContext& gc)
{	
	CL_FontDescription font_desc;
	font_desc.set_typeface_name("tahoma");
	font_desc.set_height(12);
	font_desc.set_anti_alias(false);
	smallFont = CL_Font_System(gc, font_desc);
	
	font_desc.set_height(72);
	largeFont = CL_Font_System(gc, font_desc);	

	debugOn = true;
	

	//buffer = new CL_PixelBuffer(gc.get_width(), gc.get_height(), CL_TextureFormat::cl_rgba8 );

	reset(300,350);
}

void GameLogic::mouseDown(const CL_InputEvent &event, const CL_InputState &state)
{
}

void GameLogic::keyDown(const CL_InputEvent &event, const CL_InputState &state)
{
	switch(event.id)
	{
	case CL_KEY_SPACE:
		if(gameState == RUN)
			explode();
		else if(gameState == FIRST_XP)
			reset(300,350);
			//exit(0);
		//reset(event.mouse_pos.x,600-event.mouse_pos.y);
		break;
	}
}

void GameLogic::keyUp(const CL_InputEvent &event, const CL_InputState &state)
{
}

void GameLogic::update(int delta)
{
	fps = 1000.0f/delta;
	timer += delta;

	physics->update(delta);

	if(gameState==RUN && particles.back().ttl < 0)
	{
		explode();
	}

}

void GameLogic::explode() {
	//explode
	int nParticles = 700;

	Vector3f velocity = particles.back().velocity;
	Vector3f position = particles.back().position;
	//CL_Vec2f velocity = CL_Vec2f(0,0);
	for(int i=0;i<nParticles+1;++i)
	{
		particles.push_back( Particle(1.0f, Vector3f::zero, velocity, position, 500000, CL_Colorf::aliceblue) );
		gravity.add( &particles.back() );
		explosion->add( &particles.back() );
	}
	gameState = FIRST_XP;
}

void GameLogic::drawPlane(CL_GraphicContext& gc, Plane* plane)
{	
	//draw plane
	CL_Pointf origin(plane->origin.x,plane->origin.y);
	CL_Vec2f normal(plane->normal.x,plane->normal.y);
	float height = gc.get_height();
	float width = gc.get_width();

	CL_Vec2f plane_dir(normal.y, -normal.x);

	//calculate points on screen
	CL_Pointf p1 = origin - plane_dir * (width+height);
	CL_Pointf p2 = origin + plane_dir * (width+height);


	//flip y coords so 0 is at the bottom!
	CL_Pointf cl_p1 = p1;
	CL_Pointf cl_p2 = p2;
	cl_p1.y = height - p1.y;
	cl_p2.y = height - p2.y;

	CL_Draw::line(gc, cl_p1, cl_p2, CL_Colorf::gray);

	if(debugOn) {
		//draw origin and normal
		CL_Pointf center = p1+(p2-p1)/2;
		CL_Pointf center_normal = center + normal*10;

		center.y = height - center.y;
		center_normal.y = height - center_normal.y;

		CL_Draw::line(gc, center, center_normal, CL_Colorf::gray);
	}
}

void GameLogic::drawLimitedPlane(CL_GraphicContext& gc, LimitedPlane* plane) {
	
	//flip y coords so 0 is at the bottom!
	CL_Pointf cl_p1(plane->v1.x, plane->v1.y);
	CL_Pointf cl_p2(plane->v2.x, plane->v2.y);
	CL_Vec2f normal(plane->normal.x,plane->normal.y);

	CL_Pointf center = cl_p1+(cl_p2-cl_p1)/2;
	CL_Pointf center_normal = center + normal*10;

	float h = gc.get_height();
	cl_p1.y = h - cl_p1.y;
	cl_p2.y = h - cl_p2.y;
	center.y = h - center.y;
	center_normal.y = h - center_normal.y;

	CL_Draw::line(gc, cl_p1, cl_p2, CL_Colorf::gray);

	if(debugOn) {
		CL_Draw::line(gc, center, center_normal, CL_Colorf::gray);
	}
}

void GameLogic::draw(CL_GraphicContext& gc)
{		
	//draw planes
	for(std::list<LimitedPlane>::iterator P = planes.begin(); P != planes.end(); ++P)
	{
		drawLimitedPlane(gc, &*P);
	}

	//draw springs
	drawSpring(gc, spring);

	for(std::list<Particle>::iterator P = particles.begin(); P != particles.end(); ++P)
	{
		//draw particle
		if(P->ttl > 0) {
			//CL_Draw::point(gc, P->position.x, gc.get_height() - P->position.y, );
			CL_Draw::point(gc, P->position.x, gc.get_height() - P->position.y, P->color);
		}
	}

	if(debugOn)
	{
		//debug text
		smallFont.draw_text(gc, 10,10,cl_format("FPS (ms): %1",fps));
		smallFont.draw_text(gc, 10,20,cl_format("Time (ms): %1",timer));
	}
}

void GameLogic::drawSpring(CL_GraphicContext& gc, LinearSpring* spring) 
{
	float segments = 4;
	float size = 15.0f;
	CL_Colorf color = CL_Colorf::white;

	Vector3f localPos = spring->local->position;
	CL_Pointf start(localPos.x,localPos.y);
		
	Vector3f remotePos = spring->remote->position;
	CL_Pointf end(remotePos.x,remotePos.y);

	CL_Vec2f direction = end-start;
	float segmentSize = direction.length()/segments/4.0f;
	direction.normalize();
	CL_Vec2f perpendicular(-direction.y, direction.x);

	for(int i=0; i<segments; ++i)
	{
		CL_Pointf p1 = start + direction*i*segmentSize;
		CL_Pointf p2 = start + direction*i*segmentSize*2;
		p2 -= perpendicular*size;
		CL_Pointf p3 = start + direction*i*segmentSize*3;
		p2 += perpendicular*size;
		CL_Pointf p4 = start + direction*i*segmentSize*4;
		
		p1.y = gc.get_height()-p1.y;
		p2.y = gc.get_height()-p2.y;
		p3.y = gc.get_height()-p3.y;
		p4.y = gc.get_height()-p4.y;
		CL_Draw::line(gc, p1, p2, CL_Colorf::red);
		CL_Draw::line(gc, p2, p3, CL_Colorf::green);
		CL_Draw::line(gc, p3, p4, CL_Colorf::blue);
	}
}

void GameLogic::reset(float x, float y)
{
	gameState = RUN;

	
	explosion = new ForceExplosion(0.0f, 750.0f, 1.0f, 360.0f);
	gravity = Gravity(-9.81f);

	physics = new Physics();
	physics->init(&particles, &forceGenerators, &gravity);
	

	//add planes
	//Plane plane;
	//CL_Vec2f normal(0,1);
	//CL_Vec2f planeNormal = CL_Vec2f::rotate(normal, CL_Point(0,0),CL_Angle(30,cl_degrees));
	planes.clear();
	particles.clear();
	forceGenerators.clear();
	gravity.clear();

	planes.push_back(LimitedPlane(Vector3f(250,300,0), Vector3f(800,450,0), 0.7f));
	physics->addCollisionPlane(&planes.back());

	planes.push_back(LimitedPlane(Vector3f(0,400,0), Vector3f(400,200,0), 0.7f));
	physics->addCollisionPlane(&planes.back());

	//planeNormal = CL_Vec2f::rotate(normal, CL_Point(0,0),CL_Angle(-10,cl_degrees));
	planes.push_back(LimitedPlane(Vector3f(450,100,0), Vector3f(800,250,0), 0.7f));
	physics->addCollisionPlane(&planes.back());

	
	planes.push_back(LimitedPlane( Vector3f(0,10,0), Vector3f(800,10,0), 0.7f));
	physics->addCollisionPlane(&planes.back());


	forceGenerators.push_back(&gravity);
	forceGenerators.push_back(explosion);
	for(std::list<LimitedPlane>::iterator P = planes.begin(); P != planes.end(); ++P)
	{
		P->frictionGenerator->clear();
		forceGenerators.push_back(P->frictionGenerator);
	}

	particles.push_back(Particle(1.0f, Vector3f::zero, Vector3f::zero, Vector3f(400, 600, 0), INT_MAX));
	anchor = &particles.back();

	particles.push_back(Particle(1.0f, Vector3f::zero, Vector3f::zero, Vector3f(400, 550, 0), INT_MAX));
	remoteParticle = &particles.back();

	if(spring)
		delete spring;

	spring = new LinearSpring(5, 50, 70, 0.5f, anchor, remoteParticle);
	
	physics->addSpring(spring); //could be a problem when resetting, since old spring is deleted but not removed from physics
	
	//physics->addSpring(spring); 

	gravity.add(remoteParticle);
	gravity.add(anchor);

	particles.push_back(Particle(1.0f, Vector3f::zero, Vector3f(15,90,0), Vector3f(x,y,0), 3000)); 
	gravity.add(&particles.back());

	timer=0;
}

CL_ColorHSVf GameLogic::velocityToHSV(Vector3f v) {
	float temp = min(1.0f, (float) v.length() / 200);
	return CL_ColorHSVf(temp*360, 1, 1, 1);
}
